<!DOCTYPE html>
<title>Credential Manager: create() basics.</title>
<script src="../resources/testharness.js"></script>
<script src="../resources/testharnessreport.js"></script>
<script type="module">
import {AuthenticatorStatus} from '/gen/third_party/blink/public/mojom/webauthn/authenticator.mojom.m.js';
import {MockAuthenticator} from './resources/mock-navigator-credentials.js';
import {assertValidMakeCredentialResponse, ATTESTATION_OBJECT, CABLE_AUTHENTICATION, CABLE_REGISTRATION, CLIENT_DATA_JSON, deepCopy, ID, MAKE_CREDENTIAL_OPTIONS, RAW_ID} from './resources/test-inputs.js';

if (document.location.host != "subdomain.example.test:8443") {
  document.location = "https://subdomain.example.test:8443/credentialmanager/credentialscontainer-create-basics.html";
  promise_test(_ => new Promise(_ => {}), "Stall tests on the wrong host.");
}

const mockAuthenticator = new MockAuthenticator;

promise_test(t => {
    return promise_rejects_dom(t, "NotSupportedError",
        navigator.credentials.create());
}, "navigator.credentials.create() with no argument.");

promise_test(t => {
    mockAuthenticator.setAuthenticatorStatus(AuthenticatorStatus.INVALID_DOMAIN);
    return promise_rejects_dom(t, "SecurityError",
        navigator.credentials.create({publicKey : MAKE_CREDENTIAL_OPTIONS}));
}, "Verify that invalid domain error returned by mock is properly handled.");

promise_test(t => {
    mockAuthenticator.setAuthenticatorStatus(AuthenticatorStatus.ABORT_ERROR);
    return promise_rejects_dom(t, "AbortError",
        navigator.credentials.create({publicKey : MAKE_CREDENTIAL_OPTIONS}));
}, "Verify that abort error returned by mock is properly handled.");

promise_test(t => {
    var authAbortController = new AbortController();
    var authAbortSignal = authAbortController.signal;
    authAbortController.abort();
    return promise_rejects_dom(t, "AbortError",
        navigator.credentials.create({publicKey : MAKE_CREDENTIAL_OPTIONS, signal : authAbortSignal}));
}, "navigator.credentials.create() with abort signal");

promise_test(t => {
  var customMakeCredOptions = deepCopy(MAKE_CREDENTIAL_OPTIONS);
  delete customMakeCredOptions.challenge;
  return promise_rejects_js(t, TypeError,
      navigator.credentials.create({publicKey : customMakeCredOptions}));
}, "navigator.credentials.create() with missing challenge");

promise_test(t => {
  var customMakeCredOptions = deepCopy(MAKE_CREDENTIAL_OPTIONS);
  delete customMakeCredOptions.pubKeyCredParams;
  return promise_rejects_js(t, TypeError,
      navigator.credentials.create({publicKey : customMakeCredOptions}));
}, "navigator.credentials.create() with missing parameters");

promise_test(t => {
  var customMakeCredOptions = deepCopy(MAKE_CREDENTIAL_OPTIONS);
  delete customMakeCredOptions.rp;
  return promise_rejects_js(t, TypeError,
      navigator.credentials.create({publicKey: customMakeCredOptions}));
}, "navigator.credentials.create() with missing rp");

promise_test(t => {
  var customMakeCredOptions = deepCopy(MAKE_CREDENTIAL_OPTIONS);
  delete customMakeCredOptions.user;
  return promise_rejects_js(t, TypeError,
      navigator.credentials.create({publicKey: customMakeCredOptions}));
}, "navigator.credentials.create() with missing user");

promise_test(t => {
  var customMakeCredOptions = deepCopy(MAKE_CREDENTIAL_OPTIONS);
  delete customMakeCredOptions.rp.name;
  return promise_rejects_js(t, TypeError,
      navigator.credentials.create({publicKey: customMakeCredOptions}));
}, "navigator.credentials.create() with missing rp.name");

promise_test(t => {
  var customMakeCredOptions = deepCopy(MAKE_CREDENTIAL_OPTIONS);
  delete customMakeCredOptions.user.id;
  return promise_rejects_js(t, TypeError,
      navigator.credentials.create({publicKey: customMakeCredOptions}));
}, "navigator.credentials.create() with missing user.id");

promise_test(t => {
  var customMakeCredOptions = deepCopy(MAKE_CREDENTIAL_OPTIONS);
  delete customMakeCredOptions.user.name;
  return promise_rejects_js(t, TypeError,
      navigator.credentials.create({publicKey: customMakeCredOptions}));
}, "navigator.credentials.create() with missing user.name");

promise_test(t => {
  var customMakeCredOptions = deepCopy(MAKE_CREDENTIAL_OPTIONS);
  delete customMakeCredOptions.user.displayName;
  return promise_rejects_js(t, TypeError,
      navigator.credentials.create({publicKey: customMakeCredOptions}));
}, "navigator.credentials.create() with missing user.displayName");

promise_test(_ => {
  mockAuthenticator.setRawId(RAW_ID);
  mockAuthenticator.setId(ID);
  mockAuthenticator.setClientDataJson(CLIENT_DATA_JSON);
  mockAuthenticator.setAttestationObject(ATTESTATION_OBJECT);
  mockAuthenticator.setAuthenticatorStatus(AuthenticatorStatus.SUCCESS);

  return navigator.credentials.create({publicKey : MAKE_CREDENTIAL_OPTIONS}).then(r => {
      assertValidMakeCredentialResponse(r);
  });
}, "Verify that the mock returns the values we give it.");

promise_test(t => {
  mockAuthenticator.setAuthenticatorStatus(AuthenticatorStatus.PENDING_REQUEST);
  return promise_rejects_dom(t, "OperationError",
    navigator.credentials.create({ publicKey : MAKE_CREDENTIAL_OPTIONS}));
}, "Verify that pending request error returned by mock is properly handled.");

promise_test(function (t) {
  mockAuthenticator.setAuthenticatorStatus(AuthenticatorStatus.UNKNOWN_ERROR);
  return promise_rejects_dom(t, "NotReadableError",
      navigator.credentials.create({ publicKey : MAKE_CREDENTIAL_OPTIONS}));
}, "Verify that unknown error returned by mock is properly handled.");

promise_test(t => {
  mockAuthenticator.setAuthenticatorStatus(AuthenticatorStatus.NOT_ALLOWED_ERROR);
  return promise_rejects_dom(t, "NotAllowedError",
      navigator.credentials.create({ publicKey : MAKE_CREDENTIAL_OPTIONS}));
}, "Verify that not allowed error returned by mock is properly handled.");

promise_test(t => {
  mockAuthenticator.setAuthenticatorStatus(AuthenticatorStatus.ALGORITHM_UNSUPPORTED);
  return promise_rejects_dom(t, "NotSupportedError",
      navigator.credentials.create({ publicKey : MAKE_CREDENTIAL_OPTIONS}));
}, "Verify that algorithm unsupported error returned by mock is properly handled.");

promise_test(t => {
  mockAuthenticator.setAuthenticatorStatus(AuthenticatorStatus.CREDENTIAL_EXCLUDED);
  return promise_rejects_dom(t, "InvalidStateError",
      navigator.credentials.create({ publicKey : MAKE_CREDENTIAL_OPTIONS}));
}, "Verify that credential excluded error returned by mock is properly handled.");

promise_test(_ => {
  mockAuthenticator.reset();
  mockAuthenticator.setDefaultsForSuccessfulMakeCredential();
  var customMakeCredOptions = deepCopy(MAKE_CREDENTIAL_OPTIONS);
  customMakeCredOptions.rp = { name: "Acme" };
  return navigator.credentials.create({publicKey: customMakeCredOptions}).then(r => {
      assertValidMakeCredentialResponse(r);
  });
}, "navigator.credentials.create() with missing rp.id");

promise_test(_ => {
  mockAuthenticator.reset();
  mockAuthenticator.setDefaultsForSuccessfulMakeCredential();
  var customMakeCredOptions = deepCopy(MAKE_CREDENTIAL_OPTIONS);
  delete customMakeCredOptions.authenticatorSelection;
  return navigator.credentials.create({publicKey: customMakeCredOptions}).then(r => {
      assertValidMakeCredentialResponse(r);
  });
}, "navigator.credentials.create() with missing authenticatorSelection");

promise_test(_ => {
  mockAuthenticator.reset();
  mockAuthenticator.setDefaultsForSuccessfulMakeCredential();
  var customMakeCredOptions = deepCopy(MAKE_CREDENTIAL_OPTIONS);
  delete customMakeCredOptions.authenticatorSelection.requireResidentKey;
  return navigator.credentials.create({publicKey: customMakeCredOptions}).then(r => {
      assertValidMakeCredentialResponse(r);
  });
}, "navigator.credentials.create() with missing requireResidentKey");

promise_test(_ => {
  mockAuthenticator.reset();
  mockAuthenticator.setDefaultsForSuccessfulMakeCredential();
  var customMakeCredOptions = deepCopy(MAKE_CREDENTIAL_OPTIONS);
  delete customMakeCredOptions.authenticatorSelection.userVerification;
  return navigator.credentials.create({publicKey: customMakeCredOptions}).then(r => {
      assertValidMakeCredentialResponse(r);
  });
}, "navigator.credentials.create() with missing userVerification");

promise_test(t => {
  mockAuthenticator.reset();
  mockAuthenticator.setAuthenticatorStatus(AuthenticatorStatus.EMPTY_ALLOW_CREDENTIALS);
  var customMakeCredOptions = deepCopy(MAKE_CREDENTIAL_OPTIONS);
  return promise_rejects_dom(t, "NotSupportedError",
      navigator.credentials.create({publicKey: customMakeCredOptions}));
}, "navigator.credentials.create() with requireResidentKey true");

promise_test(t => {
  mockAuthenticator.reset();
  mockAuthenticator.setAuthenticatorStatus(AuthenticatorStatus.USER_VERIFICATION_UNSUPPORTED);
  var customMakeCredOptions = deepCopy(MAKE_CREDENTIAL_OPTIONS);
  customMakeCredOptions.authenticatorSelection.userVerification = 'required';
  return promise_rejects_dom(t, "NotSupportedError",
      navigator.credentials.create({publicKey: customMakeCredOptions}));
}, "navigator.credentials.create() with userVerification required");

promise_test(_ => {
  mockAuthenticator.reset();
  mockAuthenticator.setDefaultsForSuccessfulMakeCredential();
  var customMakeCredOptions = deepCopy(MAKE_CREDENTIAL_OPTIONS);
  customMakeCredOptions.authenticatorSelection.userVerification = 'discouraged';
  return navigator.credentials.create({publicKey: customMakeCredOptions}).then(r => {
      assertValidMakeCredentialResponse(r);
  });
}, "navigator.credentials.create() with userVerification discouraged");

promise_test(t => {
  mockAuthenticator.reset();
  mockAuthenticator.setAuthenticatorStatus(AuthenticatorStatus.USER_VERIFICATION_UNSUPPORTED);
  var customMakeCredOptions = deepCopy(MAKE_CREDENTIAL_OPTIONS);
  customMakeCredOptions.authenticatorSelection.authenticatorAttachment = 'platform';
  return promise_rejects_dom(t, "NotSupportedError",
      navigator.credentials.create({publicKey: customMakeCredOptions}));
}, "navigator.credentials.create() with platform authenticatorAttachment");

promise_test(_ => {
  mockAuthenticator.reset();
  mockAuthenticator.setDefaultsForSuccessfulMakeCredential();
  var customMakeCredOptions = deepCopy(MAKE_CREDENTIAL_OPTIONS);
  customMakeCredOptions.authenticatorSelection.authenticatorAttachment = 'cross-platform';
  return navigator.credentials.create({publicKey: customMakeCredOptions}).then(r => {
      assertValidMakeCredentialResponse(r);
  });
}, "navigator.credentials.create() with cross-platform authenticatorAttachment");

promise_test(t => {
  mockAuthenticator.reset();
  mockAuthenticator.setDefaultsForSuccessfulMakeCredential();
  var customMakeCredOptions = deepCopy(MAKE_CREDENTIAL_OPTIONS);
  customMakeCredOptions.extensions = {cableRegistration: CABLE_REGISTRATION};
  return navigator.credentials.create({publicKey: customMakeCredOptions}).then(r => {
      assertValidMakeCredentialResponse(r);
  });
}, "navigator.credentials.create() with cableRegistration extension");

promise_test(t => {
  mockAuthenticator.reset();
  mockAuthenticator.setDefaultsForSuccessfulMakeCredential();
  var customMakeCredOptions = deepCopy(MAKE_CREDENTIAL_OPTIONS);
  customMakeCredOptions.extensions = {cableAuthentication: [CABLE_AUTHENTICATION]};
  return promise_rejects_dom(t, "NotSupportedError",
      navigator.credentials.create({publicKey : customMakeCredOptions}));
}, "navigator.credentials.create() with cableAuthentication extension not supported");

promise_test(t => {
  mockAuthenticator.reset();
  mockAuthenticator.setDefaultsForSuccessfulMakeCredential();
  var customMakeCredOptions = deepCopy(MAKE_CREDENTIAL_OPTIONS);
  customMakeCredOptions.extensions = {googleLegacyAppidSupport: true}
  return navigator.credentials.create({publicKey: customMakeCredOptions}).then(r => {
     assertValidMakeCredentialResponse(r);
  });
}, "navigator.credentials.create() with googleLegacyAppidSupport extension");

</script>
